package com.fuyin.service.admin.impl;

import cn.hutool.core.lang.Validator;
import com.fuyin.commons.enums.ErrorCodeEnum;
import com.fuyin.commons.exception.ApiException;
import com.fuyin.commons.response.BaseResponse;
import com.fuyin.commons.response.ErrorCode;
import com.fuyin.commons.utils.JwtTokenUtil;
import com.fuyin.commons.utils.MyUtils;
import com.fuyin.dao.*;
import com.fuyin.dto.CommonUserDetails;
import com.fuyin.dto.params.LoginParam;
import com.fuyin.dto.vo.*;
import com.fuyin.entity.*;
import com.fuyin.service.RedisService;
import com.fuyin.service.ValidateServiceImpl;
import com.fuyin.service.admin.AdminService;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yuanxilan (yuanxilan@keep.com)
 * @Description
 * @date 2021年10月26日 3:06 下午
 * @since
 */
@Service
@Slf4j
public class AdminServiceImpl implements AdminService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private GpDao gpDao;

    @Autowired
    private ZbDao zbDao;

    @Autowired
    private OperationLogDao logDao;

    @Autowired
    private ReportDao reportDao;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private ValidateServiceImpl validateService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private OperationLogDao operationLogDao;

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String from;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Value("${password.pattern}")
    private String passwordPattern;

    @Override
    public UserDetails loadUserByUsername(String userName) {
        User user = userDao.getUserByUserName(userName);
        if (Objects.nonNull(user)) {
            return new CommonUserDetails(user);
        }
        return null;
    }

    @Override
    public BaseResponse login(LoginParam param) {
        if (Objects.isNull(param) || StringUtils.isBlank(param.getUserName()) || StringUtils.isBlank(param.getPassword())) {
            log.error("com.fuyin.service.admin.impl.AdminServiceImpl#login param:{}", param);
            return BaseResponse.error(ErrorCodeEnum.BAD_REQUEST);
        }
        String token = "";
        User user = userDao.getUserByUserName(param.getUserName());
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "账号不存在");
        } else if (user.getState() == -1) {
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_USED, "账号未启动");
        } else if (!passwordEncoder.matches(param.getPassword(), user.getPassword())) {
            return BaseResponse.error(ErrorCode.ACCOUNT_ERROR, "账号密码错误");
        } else if (user.getType() != 1) {
            return BaseResponse.error(ErrorCode.FORBIDDEN, "非用户请求用户接口");
        }
        if (redisService.exists(user.getUserName())) {
            redisService.delete(redisService.get(user.getUserName()));
        }
        CommonUserDetails userDetails = new CommonUserDetails(user);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        token = jwtTokenUtil.generateToken(userDetails);
        redisService.set(token, user.getType());
        redisService.set(user.getUserName(), token);
        return BaseResponse.success(new LoginResult(token, user.getType()));
    }

    @Override
    public BaseResponse logout(String token) {
        token = StringUtils.substring(token, tokenHead.length());
        redisService.delete(token);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse updateUserPassword(String token, String userName, String password, HttpServletRequest request) {
        token = token.substring(this.tokenHead.length());
        OperationLog operationLog = new OperationLog();
        operationLog.setUserName(jwtTokenUtil.getUserNameFromToken(token));
        operationLog.setType("操作日志");
        operationLog.setOperationTime(new Date());
        operationLog.setDescription("修改用户密码，用户名为: " + userName);
        operationLog.setIp(MyUtils.getIpAddr(request));
        MyUtils.checkParams("账号、密码不能为空", userName, password);
        User user = userDao.getUserByUserName(userName);
        if (Objects.isNull(user)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCodeEnum.BAD_REQUEST.getErrorCode(), "账号不存在");
        } else if (user.getType() != 0) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.FORBIDDEN, "只能修改用户密码");
        } else if (passwordEncoder.matches(password, user.getPassword())) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.ACCOUNT_NEW_OLD_PSW_SAME, "新旧密码不能相同");
        }
        if (!password.matches(passwordPattern)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.PASSWORD_NOT_MATCH_PATTERN, "密码不符合规范");
        }
        password = passwordEncoder.encode(password);
        userDao.updatePassword(userName, password);
        if (redisService.exists(userName)) {
            redisService.delete(redisService.get(userName));
            redisService.delete(userName);
        }
        operationLog.setResult("修改成功");
        logDao.save(operationLog);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse updateZB(ArrayList<ZdyzbDto> zdyzbs) {
        log.info("zdyzbs: {}", zdyzbs);
        gpDao.updateZB(getZb(zdyzbs, "zzczzts").getBzz(), getZb(zdyzbs, "chzzts").getBzz(),
                getZb(zdyzbs, "yszkzzts").getBzz(), getZb(zdyzbs, "toazzl").getBzz(),
                getZb(zdyzbs, "chzzl").getBzz(), getZb(zdyzbs, "yszkzzl").getBzz(),
                getZb(zdyzbs, "roejq").getBzz(), getZb(zdyzbs, "zzcjll").getBzz(),
                getZb(zdyzbs, "xsmll").getBzz(), getZb(zdyzbs, "xsjll").getBzz(),
                getZb(zdyzbs, "ld").getBzz(), getZb(zdyzbs, "sd").getBzz(),
                getZb(zdyzbs, "xjllb").getBzz(), getZb(zdyzbs, "zcfzl").getBzz(),
                getZb(zdyzbs, "zbzzl").getBzz(), getZb(zdyzbs, "qsjzbl").getBzz(),
                getZb(zdyzbs, "lszfbs").getBzz(), getZb(zdyzbs, "dbnl").getBzz(), new Date());
        for (ZdyzbDto zdyzbDto : zdyzbs) {
            if (StringUtils.isNotBlank(zdyzbDto.getKey()) && Objects.nonNull(zdyzbDto.getNormal()) && Objects.nonNull(zdyzbDto.getUnnormal())) {
                ZdyzbFz zdyzbFz = new ZdyzbFz(zdyzbDto.getKey(), zdyzbDto.getNormal(), zdyzbDto.getUnnormal());
                zbDao.updateFz(zdyzbFz);
            }
        }
        return BaseResponse.success();
    }

    private static ZdyzbDto getZb(ArrayList<ZdyzbDto> zdyzbs, String key) {
        List<ZdyzbDto> list = zdyzbs.stream().filter(x -> x.getKey().equals(key)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return new ZdyzbDto();
        }
        return list.get(0);
    }

    @Override
    public BaseResponse updateZdyzbModel(String token, ZdyzbModel zdyzbModel) {
        if (Objects.isNull(zdyzbModel.getZbKey()) && Objects.isNull(zdyzbModel.getEquation())
                && Objects.isNull(zdyzbModel.getScore())) {
            return BaseResponse.error(400, "不能所有指标都为空");
        }
        if (Objects.isNull(zdyzbModel.getType())) {
            return BaseResponse.error(400, "类型不能为空");
        }
        if (!(zdyzbModel.getType() >= 0 && zdyzbModel.getType() <= 2)) {
            return BaseResponse.error(400, "类型有误");
        }
        zbDao.updateZdyzbModel(zdyzbModel.getZbKey(), zdyzbModel.getEquation(), zdyzbModel.getScore(), zdyzbModel.getType());
        return BaseResponse.success();
    }

    @Override
    public BaseResponse updateRzppd(String token, List<Rzppd> rzppds) {
        if (Objects.isNull(rzppds)) {
            throw new ApiException("参数不全");
        }
        for (Rzppd newRzppd : rzppds) {
            if (Objects.isNull(newRzppd.getCznl()) && Objects.isNull(newRzppd.getYlnl())
                    && Objects.isNull(newRzppd.getBz()) && Objects.isNull(newRzppd.getPpd())) {
                return BaseResponse.error(400, "不能所有指标都为空");
            }
        }
        zbDao.deleteAllRzppd();
        for (Rzppd newRzppd : rzppds) {
            zbDao.updateRzppd(newRzppd.getCznl(), newRzppd.getYlnl(), newRzppd.getBz(), newRzppd.getPpd());
        }
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<ArrayList<Rzppd>> getRzppd(String token) {
        User user = null;
        ArrayList<Rzppd> rzppds = (ArrayList<Rzppd>) zbDao.getRzppd();
        return BaseResponse.success(rzppds);
    }

    @Override
    public BaseResponse<ArrayList<ReportInfo>> getReportInfo(String token, Integer pageSize, Integer pageNum, Integer type) {
        MyUtils.checkParams("pageSize 和 pageNum 不能为空", pageSize, pageNum);
        if (pageSize <= 0 || pageNum <= 0) {
            return BaseResponse.error(ErrorCode.BAD_REQUEST, "pageNum 和 pageSize 不能小于 0");
        }
        String userName = jwtTokenUtil.getUserNameFromToken(token.substring(this.tokenHead.length()));
        if (StringUtils.isBlank(userName)) {
            return BaseResponse.error(ErrorCode.UNAUTHORIZED, "token 失效，请重新登录");
        }
        User user = userDao.getUserByUserName(userName);
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "账号不存在");
        }
        PageHelper.startPage(pageNum, pageSize);
        ArrayList<ReportInfo> reportInfos = (ArrayList<ReportInfo>) reportDao.getReportInfosByUserIdAndType(user.getId(), type);
        for (ReportInfo reportInfo : reportInfos) {
//            String replaceText = reportInfo.getInfo().replaceAll("\\\\","");
//            String parseText = replaceText.substring(1,replaceText.length()-1);
//            Object parse = JSONObject.parse(reportInfo.getInfo());
//            log.info("parse: {}", parse);
//            String sJsonOne = parse.toString();
//            reportInfo.setReport(JSON.parseObject(sJsonOne, RiskReport.class));
//            reportInfo.setReport(JSON.parseObject(StringEscapeUtils.unescapeJava(JSON.toJSONString(reportInfo.getInfo())), RiskReport.class));
//            reportInfo.setReport(JSON.parseObject(parse, RiskReport.class));
//            reportInfo.setReport(JSONObject.parseObject(JSON.parse(reportInfo.getInfo()).toString(), RiskReport.class));
//            reportInfo.setReport(reportInfo);
            try {
                reportInfo.setReport(MyUtils.byteToObj(reportInfo.getInfo()));
                reportInfo.setInfo(new byte[]{});
            } catch (Exception e) {
                log.error("com.fuyin.service.admin.impl.AdminServiceImpl.getReportInfo e:{}, user:{}", e.getMessage(), user);
            }
        }
        return BaseResponse.success(reportInfos);
    }

    @Override
    public BaseResponse<ArrayList<User>> getAllUsers(String token, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userDao.getUserList(Arrays.asList(0, 1, 2));
        ArrayList<User> ans = (ArrayList<User>) users;
        ans.forEach(x -> x.setPassword(""));
        ans.forEach(x -> x.setHobby(""));
        return BaseResponse.success(ans);
    }

    @Override
    public BaseResponse updateUserInfo(String token, User user, HttpServletRequest request) {
        MyUtils.checkParams("用户名不能为空", user.getUserName());
        token = token.substring(this.tokenHead.length());
        OperationLog operationLog = new OperationLog();
        operationLog.setUserName(jwtTokenUtil.getUserNameFromToken(token));
        operationLog.setType("操作日志");
        operationLog.setOperationTime(new Date());
        operationLog.setDescription("修改用户信息，用户名为: " + user.getUserName());
        operationLog.setIp(MyUtils.getIpAddr(request));
        User user1 = userDao.getUserByUserName(user.getUserName());
        if (Objects.isNull(user1)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "查找不到该用户");
        }
        if (Objects.nonNull(user.getType()) && (user.getType() < 0 || user.getType() > 2)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(400, "错误的用户类型");
        }
        if (user1.getType() > 0) {
            operationLog.setResult("修改时间");
            logDao.save(operationLog);
            return BaseResponse.error(400, "不能修改管理员和超级管理员信息");
        }
        if (Objects.nonNull(user.getType()) && user.getType() == 2) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(400, "无权修改为超级管理员");
        }
//        user.setPassword("");
        if (Objects.nonNull(user.getEmail()) && !StringUtils.equals(user.getEmail(), user1.getEmail())) {
            String email = user.getEmail();
            try {
                if (validateService.sendValidateLimitation(email, 20, 1)) {
                    // 若允许重置密码，则在pm_validate表中插入一行数据，带有token
                    PmValidate pmValidate = new PmValidate();
                    validateService.insertNewResetRecord(pmValidate, user, UUID.randomUUID().toString(), "changeEmail");
                    // 设置邮件内容
                    String appUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/fuyin/api/change_email";
                    MimeMessage mimeMessage = mailSender.createMimeMessage();
                    // multipart模式
                    MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "utf-8");
                    mimeMessageHelper.setTo(email);
                    mimeMessageHelper.setFrom(from);
                    mimeMessageHelper.setSubject("修改邮箱");
                    StringBuilder sb = new StringBuilder();
                    sb.append("<html><head></head>");
                    sb.append("<body><h1>点击下面的链接确认修改邮箱，有效期为 5 分钟</h1>" +
                            "<a href = " + appUrl + "?token=" + pmValidate.getResetToken() + ">" + appUrl + "?token=" + pmValidate.getResetToken() + "</a></body>");
                    sb.append("</html>");
                    // 启用html
                    mimeMessageHelper.setText(sb.toString(), true);
                    validateService.sendPasswordResetEmail(mimeMessage);
                    redisService.set(pmValidate.getResetToken(), user);
                } else {
                    return BaseResponse.error(ErrorCode.OPERATION_FREQUENCY, "操作过于频繁，请稍后再试！");
                }
            } catch (Exception e) {
                log.error("com.fuyin.service.admin.impl.AdminServiceImpl.updateUserInfo e:{}", e.getMessage());
            }
        }
        user.setEmail("");
        if (StringUtils.isNotBlank(user.getPassword()) && !Validator.hasChinese(user.getPassword())) {
            if (!user.getPassword().matches(passwordPattern)) {
                user.setPassword(StringUtils.EMPTY);
            } else {
                user.setPassword(passwordEncoder.encode(user.getPassword()));
                if (redisService.exists(user.getUserName())) {
                    redisService.delete(redisService.get(user.getUserName()));
                    redisService.delete(user.getUserName());
                }
            }
        }
        userDao.updateUser(user);
        operationLog.setResult("修改成功");
        logDao.save(operationLog);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<ArrayList<OperationLog>> getOperationLogs(String token, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        ArrayList<OperationLog> operationLogs = (ArrayList<OperationLog>) operationLogDao.getOperationLogs();
        return BaseResponse.success(operationLogs);
    }

    @Override
    public BaseResponse<ArrayList<Fxb>> getFxbs() {
        return BaseResponse.success((ArrayList<Fxb>) zbDao.getFxbs());
    }

    @Override
    public BaseResponse updateFxbs(ArrayList<Fxb> fxbs) {
        if (CollectionUtils.isEmpty(fxbs)) {
            return BaseResponse.error(ErrorCode.BAD_REQUEST, "参数不全");
        }
        zbDao.deleteAllFxbs(fxbs.get(0).getType());
        for (Fxb fxb : fxbs) {
            zbDao.insertFxb(fxb.getType(), fxb.getZfz(), fxb.getFxd(), fxb.getJy());
        }
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<ZbDto> getZB() {
        Zdyzb zdyzb = zbDao.getZdyzb();
        ArrayList<ZdyzbFz> zdyzbFzs = zbDao.getZdyzbFzs();
        ZbDto zbDto = new ZbDto();
        zbDto.setZdyzb(zdyzb);
        zbDto.setZdyzbFzs(zdyzbFzs);
        return BaseResponse.success(zbDto);
    }

    @Override
    public BaseResponse<ArrayList<ZdyzbModel>> getZdyzbModel() {
        ArrayList<ZdyzbModel> zdyzbModels = (ArrayList<ZdyzbModel>) zbDao.getZdyzbModel();
        return BaseResponse.success(zdyzbModels);
    }

    @Override
    public BaseResponse<User> getUserInfo(String token) {
        token = token.substring(this.tokenHead.length());
        String userName = jwtTokenUtil.getUserNameFromToken(token);
        User user = userDao.getUserByUserName(userName);
        user.setPassword("");
        return BaseResponse.success(user);
    }

}
